#!/usr/bin/env python3
import os
import sys
from random import choices, randint

CURDIR = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(CURDIR, "helpers"))

from pure_http_client import ClickHouseClient

client = ClickHouseClient()

N = 10
create_query = (
    "CREATE TABLE t_cnf_fuzz("
    + ", ".join([f"c{i} UInt8" for i in range(N)])
    + ") ENGINE = Memory"
)

client.query("DROP TABLE IF EXISTS t_cnf_fuzz")
client.query(create_query)

# Insert all possible combinations of bool columns.
insert_query = "INSERT INTO t_cnf_fuzz VALUES "
for i in range(2**N):
    values = []
    cur = i
    for _ in range(N):
        values.append(cur % 2)
        cur //= 2

    insert_query += "(" + ", ".join(map(lambda x: str(x), values)) + ")"

client.query(insert_query)

# Let's try to covert DNF to CNF,
# because it's a worst case in a sense.

MAX_CLAUSES = 10
MAX_ATOMS = 5


def generate_dnf():
    clauses = []
    num_clauses = randint(1, MAX_CLAUSES)
    for _ in range(num_clauses):
        num_atoms = randint(1, MAX_ATOMS)
        atom_ids = choices(range(N), k=num_atoms)
        negates = choices([0, 1], k=num_atoms)
        atoms = [
            f"(NOT c{i})" if neg else f"c{i}" for (i, neg) in zip(atom_ids, negates)
        ]
        clauses.append("(" + " AND ".join(atoms) + ")")

    return " OR ".join(clauses)


select_query = (
    "SELECT count() FROM t_cnf_fuzz WHERE {} SETTINGS convert_query_to_cnf = {}"
)

fail_report = """
Failed query: '{}'.
Result without optimization: {}.
Result with optimization: {}.
"""

T = 500
for _ in range(T):
    condition = generate_dnf()

    query = select_query.format(condition, 0)
    res = client.query(query).strip()

    query_cnf = select_query.format(condition, 1)
    res_cnf = client.query(query_cnf).strip()

    if res != res_cnf:
        print(fail_report.format(query_cnf, res, res_cnf))
        exit(1)

client.query("DROP TABLE t_cnf_fuzz")
print("OK")
